﻿/*****************************************
 * LCD.Display
 * Class for LCD.NET Framework 
 * version: 1.3.0
 * 
 * written by: Jason Pruitt
 * contact: lcd-net-framework@gmail.com
 * date: 03/19/2009
 * 
 * description:
 * Provides a .NET Class for interacting with
 * a Hitachi HD44780 or similiar LCD screen thru
 * a rs232 serial interface
 * 
 * requires:
 *      - use of PIC16F690 with LCD.NET.ASM code
 *      - Microsoft .NET Framework 2.0
 *      
 * Copyright ©2009 Jason Pruitt
 * 
 *  This file is part of the LCD.NET Framework.

    LCD.NET Framework is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    LCD.NET Framework is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with LCD.NET Framework.  If not, see <http://www.gnu.org/licenses/>.

 *****************************************/

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.IO.Ports;

namespace LCD
{
    /// <summary>
    /// Display Class for LCD.NET
    /// </summary>
    public class Display : ILCD
    {

        // Serial port connection
        private SerialPort port;

        #region Variables LCD configuration info
        private int set_segment_num = 2;            // number of segments 1 or 2
        private int display_lines = 4;              // height of LCD
        private int display_line_length = 39;       // width of LCD -1 
        private string segment_h = "both";          // segment to use
        #endregion
 
        #region Variables DDRAM numbers
        // line(X)Tag and line(X)Offset is the DDRAM address + 128 
        // of the first position of each line, line(X)Tag should
        // equal line(X)Offset
        private byte[] line1Tag = { 128 };          // set line 1
        private byte[] line2Tag = { 192 };          // set line 2 
        private byte[] line3Tag = { 128 };          // set line 3 
        private byte[] line4Tag = { 192 };          // set line 4 

        private int line1Offset = 128;              // offset line 1
        private int line2Offset = 192;              // offset line 2
        private int line3Offset = 128;              // offset line 3
        private int line4Offset = 192;              // offset line 4

        // line(X)ToZero is the difference between 128 and the 
        // corrisponding line(X)Tag and line(X)Offset numbers
        private int line1ToZero = 0;                // offset line 1
        private int line2ToZero = 64;               // offset line 2
        private int line3ToZero = 0;                // offset line 3 
        private int line4ToZero = 64;               // offset line 4   
        #endregion
		
        #region Variables Main Addressing codes for microcontroller
        private byte[] charTag = { 15 };            // start stop text tag
        private byte[] instTag = { 14 };            // set instruction tag
        private byte[] e2Tag = { 13 };              // set bottom segment tag
        private byte[] e1Tag = { 12 };              // set top segment tag
        private byte[] readCharTag = { 11 };        // read character
        private byte[] readAddressCounterTag = { 10 };   // read address counter
        private byte[] writeCGRAMDataTag = { 9 };   // set CGRAM data
        private byte[] backlightOffTag = { 8 };       // set backlight off
        private byte[] backlightOnTag = { 7 };        // set backlight on
        private byte[] clearTag = { 1 };             // set clear instruction
        #endregion
		
        #region instruction variables
        private byte[] cursorRightTag = { 20 };     // cursor right
        private byte[] cursorLeftTag = { 16 };      // cursor left
        private byte[] cursorHomeTag = { 2 };       // cursor home
        private byte[] displayRightTag = { 28 };    // cursor right
        private byte[] displayLeftTag = { 24 };     // cursor left
        #endregion

        #region Variables Properties
        // variables for properties xxxx_h
        private int line_h = 1 ;
        private string display_direction_h = "right" ;
        private bool display_shift_h = false ;
        private bool display_on_h = true ;
        private bool display_underline_h = false ;
        private bool display_blink_h = false;
        private bool backlight_on_h = true;
        #endregion

        #region LCD Connection (serial port)

        /// <summary>
        /// Connect to Microcontroller thru Serial Port
        /// </summary>
        /// <param name="com">String "COMx" port</param>
        /// <returns>True if SerialPort set up</returns>
        public bool connect(string com)
        {
            try
            {
                // Instantiate the communications
                // port with some basic settings 
                port = new SerialPort(com, 2400, Parity.None, 8, StopBits.One);
                // Write a string 
                port.Handshake = Handshake.None;
                // Open the port for communications
                port.Open();
                return true;
            }
            catch 
            {
                return false;
            }
                
        }

        /// <summary>
        /// Connect to Microcontroller thru Serial Port on "COM1"
        /// </summary>
        /// <returns>True if SerialPort set up</returns>
        public bool connect()
        {
           try
            {
                // Instantiate the communications
                // port with some basic settings 
                port = new SerialPort("COM1", 2400, Parity.None, 8, StopBits.One);
                // Write a string 
                port.Handshake = Handshake.None;
                // Open the port for communications
                port.Open();
            return true;
            }
            catch 
           {
               return false;
           }
        }

        /// <summary>
        /// Close Serial Port
        /// </summary>
        public void close()
        {
            port.Close();
        }
  
        #endregion

        #region Set Up Display (segment)

        /// <summary>
        /// Set up LCD functions
        /// </summary>
        /// <param name="s8bit">Bool True for 8 bit mode False for 4 bit mode (hardware specific)</param>
        /// <param name="s2line">Bool True for 2 line mode False for 1 line mode</param>
        /// <param name="s5x11">Bool True for 5x11 mode False for 5x8 font size</param>
        /// <param name="s2seg">Bool True for 2 segment LCD screens (hardware specific)</param>
        /// <param name="sLCDHeight">Int Number of Lines on Display</param>
        /// <param name="sLCDWidth">Int Length of Display lines</param>
        public void display_setup(bool s8bit, bool s2line, bool s5x11, bool s2seg, int sLCDHeight, int sLCDWidth) 
        {
            // initial instruction number
            int intNum = 32;
            // set up instruction number
            if (s8bit) { intNum += 16; }
            if (s2line) { intNum += 8; }
            if (s5x11) { intNum += 4; }

            // turn into byte array
            byte[] instNum = { (byte)intNum };

            // send instruction
            send_instruction(instNum);

            // set number of segments
            if (s2seg)
            {
                set_segment_num = 2;
            }
            else
            {
                set_segment_num = 1;
            }
            // number of display lines
            display_lines = sLCDHeight;
            display_line_length = sLCDWidth-1;
            commit_display_configure();
        }
  
        /// <summary>
        /// Calculates ddram address' for internal software use
        /// </summary>
        private void commit_display_configure()
        { 
            // regardless of configuration
            // set first and second line ddram numbers
            line1Offset = 128;
            line1ToZero = 0;
            line2Offset = 192;
            line2ToZero = 64;

            // if LCD has 2 segments configure line 3 & 4
            // same as 1 & 2
            if (set_segment_num == 2)
            {
                line3Offset = 128;
                line4Offset = 192;
                line3ToZero = 0;
                line4ToZero = 64;
            }
            // if 4 line 1 segment configure line 3 & 4 ddram address
            else if(display_lines == 4 && set_segment_num == 1)
            {
                line3Offset = line1Offset + display_line_length + 1;
                line4Offset = line2Offset + display_line_length + 1;
                line3ToZero = line1ToZero + display_line_length + 1;
                line4ToZero = line2ToZero + display_line_length + 1;            
            
            }

         // load instruction routine tag values
        line1Tag[0] = (byte)line1Offset;  // set line 1     [2 seg line 1 128]  [1 seg line 1 128]
        line2Tag[0] = (byte)line2Offset;  // set line 2     [2 seg line 2 192]  [1 seg line 2 192]
        line3Tag[0] = (byte)line3Offset;  // set line 3     [2 seg line 3 128]  [1 seg line 3 148]
        line4Tag[0] = (byte)line4Offset;  // set line 4     [2 seg line 4 192]  [1 seg line 4 212]        
        
        }

        #endregion

        #region Display Style - Direction, Shift, Display On, Underline, Blink, Line Length (segment)
        
        /// <summary>
        /// Gets display line length
        /// </summary>
        public int line_length 
        {
            get { return display_line_length + 1; }
        }

        /// <summary>
        /// Set or Get Display Direction, Right or Left
        /// </summary>
        /// <returns>"Right" or "Left"</returns>
        public string display_direction
        {
            get { return display_direction_h; }
            set
            {
                display_direction_h = value.ToLower();
                commit_movement();
            }
        }

        /// <summary>
        /// Set or Get Display Shift, On or Off
        /// </summary>
        /// <returns>Bool True if On False if Off</returns>
        public bool display_shift
        {
            get { return display_shift_h; }
            set
            {
                display_shift_h = value;
                commit_movement();
            }
        }
   
        /// <summary>
        /// Send Display Direction and Shift Value to microcontroller
        /// </summary>
        private void commit_movement()
        {
            // initial instruction number
            int instNum = 4;
            // check which direction ad 2 to instNum if right, 0 if left
            // default catch is to the right
            if (display_direction.ToLower() == "right") { instNum += 2; }
            else if (display_direction.ToLower() == "left"){ instNum += 0; }
            
            // check if shifting
            if (display_shift) { instNum += 1; }
            // put in byte array
            byte[] instSend = {(byte)instNum};

            // send to instruction
            send_instruction(instSend);
        }

        /// <summary>
        /// Set or Get LCD Display On or Off
        /// </summary>
        /// <returns>True if On False if Off</returns>
        public bool display_on {
            get { return display_on_h; }
            set{
                display_on_h = value;
                commit_display_style();
            }
        }

        /// <summary>
        /// Set or Get Cursor Underline On or Off
        /// </summary>
        /// <returns>True if on False if off</returns>
        public bool display_underline
        {
            get { return display_underline_h ; }
            set
            {
                display_underline_h = value;
                commit_display_style();
            }
        }

        /// <summary>
        /// Set or Get Cursor Blink On or Off
        /// </summary>
        /// <returns>True if on False if off</returns>
        public bool display_blink
        {
            get { return display_blink_h; }
            set
            {
                display_blink_h = value;
                commit_display_style();
            }
        }
     
        /// <summary>
        /// Send Display On, Underline, and Blink Value to microcontroller
        /// </summary>
        private void commit_display_style() {
            // initial instruction number 
            int instNum = 8;
            // add to initial number to turn on features
            if (display_on) { instNum += 4; }
            if (display_underline) { instNum += 2; }
            if (display_blink) { instNum += 1; }

            // put in byte array
            byte[] instSend = { (byte)instNum };
            // send to instruction
            send_instruction(instSend);
        }

        #endregion      

        #region User Defined Character Creation (line)
   
        /// <summary>
        /// Set CGRAM Address in microcontroller
        /// </summary>
        /// <param name="addr">Int address of User Defined Character</param>
        /// <return>True if Address of CGRAM 0-14</return>
        private bool set_CGRAM_address(int addr) 
        {
            // check if CCGRAM address
            if (addr < 15 && addr >= 0)
            {  
                // initial instruction number
                int intNum = 64;
                
                // turn address 0-14 into CGRAM address
                intNum += addr*8;
                
                // convert to byte value
                byte[] instNum = { (byte)intNum };

                // avoid segment
                commit_instruction(instNum);
                
                return true;
            }
            else
            {
                return false;
            }
          }

        /// <summary>
        /// Create User Defined Characters
        /// </summary>
        /// <param name="addr">int address in CGRAM to use 1-14 depending on LCD hardware</param>
        /// <param name="char_design">bool[,] 8,5 array containing character design</param>
        public void set_user_defined_character(int addr, bool[,] char_design)
        {
            /* what the byte array looks like true values are shown on display
            bool[,] char_design = {
                                  {false,false,false,false,false}, // line 1
                                  {false,false,false,false,false}, // line 2
                                  {false,false,false,false,false}, // line 3
                                  {false,false,false,false,false}, // line 4
                                  {false,false,false,false,false}, // line 5
                                  {false,false,false,false,false}, // line 6
                                  {false,false,false,false,false}, // line 7
                                  {false,false,false,false,false}  // line 8 usually left clear for cursor
                                  };// dot 1,2,3,4,5 true equals shows, false is blank
                                                    */
            // get current cursor position
            // at end will set cursor back to previous position
            int getCursorPosition = get_cursor_position();


            // byte for each line of display character
            byte[] byteLine = new byte[1];
            
            // initiate user defined characters
            set_CGRAM_address(addr);
            Thread.Sleep(1);

            // interate thru char_design array
            // changing booleen values to numeric
            // 8 rows, with 5 dots each
            for (int i = 0; i < 8; i++) {

                // initial instruction number
                int intNum = 0;
                
                // index number
                int k = 0;
                
                // go thru each line in reverse
                // 2 exp array position
                for (int j = 4;  j >= 0; j--)
                {
                    // if value is true add to instruction number
                    if (char_design[i, j] == true) {
                        intNum += Convert.ToInt16(Math.Pow(2, k));
                    }
                    k++;
                }
                // at end of row, send data to microcontroller
                byteLine[0] = (byte)intNum;
                commit_user_defined_character(byteLine); 
            }
            // when done working thru all 8 rows
            // set LCD to ddram to end user defined creation

            // return cursor to previous position and end
            line = line;
            move_cursor_to(getCursorPosition);
        }
        
        /// <summary>
        /// Used for User Defined Character creation
        /// </summary>
        /// <param name="byteLine">byte array value of user defined character line</param>
        private void commit_user_defined_character(byte[] byteLine)
        {
            // special hardware instruction for user defined creation
            port.Write(writeCGRAMDataTag, 0, 1);
            Thread.Sleep(1);

            // write to LCD
            port.Write(byteLine, 0, 1);
            Thread.Sleep(1);
        }
       
        #endregion

        #region LCD Instruction (segment resets to line)
 
        /// <summary>
        /// Write Instruction to LCD
        /// </summary>
        /// <param name="instruction">instruction xxxxTag</param>
        private void send_instruction(byte[] instruction)
        {
            // get current position 
            int getCursorPosition = get_cursor_position();
            // test which segment, then sent instruction to it
            switch (segment.ToLower())
            {
                case "top":
                    port.Write(e1Tag, 0, 1);
                    // send instruction
                    commit_instruction(instruction);
                    break;

                case "bottom":
                    port.Write(e2Tag, 0, 1);
                    // send instruction
                    commit_instruction(instruction);
                    break;

                case "both":
                    // send instruction
                    port.Write(e1Tag, 0, 1);
                    Thread.Sleep(1);
                    commit_instruction(instruction);
                    
                    // send instruction
                    port.Write(e2Tag, 0, 1);
                    Thread.Sleep(1);
                    commit_instruction(instruction); 
                    break;
            }
            // return display to current line

            line = line;
            move_cursor_to(getCursorPosition);
        }
        
        /// <summary>
        /// For instruction functions sends data to microcontroller
        /// </summary>
        /// <param name="instruction"></param>
        private void commit_instruction(byte[] instruction)
        {
            port.Write(instTag, 0, 1);
            Thread.Sleep(1);

            port.Write(instruction, 0, 1);
            Thread.Sleep(1);
        }
  
        #endregion  
        
        #region Segment and lines

        /// <summary>
        /// Set or Get current LCD Line number
        /// </summary>
        /// <returns>line number</returns>
        public int line 
        {
            get { return line_h; }
            set {
                line_h = value;
                commit_line(line_h);    
                }
        }

        /// <summary>
        /// For sending Line and Top/Bottom LCD Segment value to microcontroller
        /// </summary>
        /// <param name="line">line number</param>
        private void commit_line(int line) {
            byte[] lineByte = new byte[1];
            byte[] segByte = new byte[1];
            switch (line)
            {
                case 1:
                    segByte = e1Tag;
                    lineByte = line1Tag;
                    break;
                case 2:
                    segByte = e1Tag ;
                    lineByte = line2Tag;
                    break;
                case 3:
                    if (set_segment_num == 2) { segByte = e2Tag; } else { segByte = e1Tag; }
                    lineByte = line3Tag;
                    break;
                case 4:
                    if (set_segment_num == 2) { segByte = e2Tag; } else { segByte = e1Tag; }
                    lineByte = line4Tag ;
                    break;
            }
            // send instruction
            port.Write(segByte, 0, 1);
            Thread.Sleep(1);

            port.Write(instTag, 0, 1);
            Thread.Sleep(1);

            port.Write(lineByte, 0, 1);
            Thread.Sleep(1);
        }

        /// <summary>
        /// Get or Set value of which segment of LCD screen to use "Top", "Bottom", or "Both">
        /// </summary>
        /// <returns>name of segment</returns>
        public string segment
        {
            get { return segment_h; }
            set { segment_h = value; }
        }
 
        #endregion       

        #region Move Display (segment)

        /// <summary>
        /// Move entire contents of display Left or Right
        /// </summary>
        /// <param name="move">"Right" "Left"</param>
        public void move_display(string move)
        {
            // find which type of movement
            // uses segment
            switch (move.ToLower())
            {
                case "right":
                    send_instruction(displayRightTag);
                    break;
                case "left":
                    send_instruction(displayLeftTag);
                    break;
            }
        }
  
        #endregion

        #region Clear Display (segment)

        /// <summary>
        /// Clear Display of Characters
        /// </summary>
        public void clear_display()
        {
            send_instruction(clearTag);
            Thread.Sleep(2); 
        }

        #endregion       

        #region Clear lines
        /// <summary>
        /// <para>Clear Current Display Line</para>
        /// </summary>
        public void clear_line() 
        {
            string displayClear = " ";

            for (int i = 0; i < display_line_length; i++) 
            {
                displayClear += " ";
            }
            write(displayClear);
            this.line = this.line;
        }

        /// <summary>
        /// <para>Clear Current Display Line</para>
        /// </summary>
        /// <param name="line">Line Number</param>
        public void clear_line(int line)
        {
            this.line = line;
            string displayClear = " ";

            for (int i = 0; i < display_line_length; i++)
            {
                displayClear += " ";
            }
            write(displayClear);
            this.line = line;
        }
        #endregion

        #region Move Cursor (line)

        /// <summary>
        /// Move Cursor From Current Position "Right" "Left" or "Home"
        /// </summary>
        /// <param name="move">"Right" "Left" or "Home"</param>
        public void move_cursor(string move)
        {
            int curPos = get_cursor_position();
            // find which type of movement
            // uses commit_instruction to avoid segment
            switch (move.ToLower()) { 
                case "right":
                    if (curPos < display_line_length)
                    {
                        commit_instruction(cursorRightTag);
                    }
                    else if (curPos >= display_line_length)
                    {
                        move_cursor_to(0);
                    }
                    break;
                case "left":
                    if (curPos > 0 && curPos <= display_line_length)
                    {
                        commit_instruction(cursorLeftTag);
                    }
                    else if (curPos <= 0 || curPos > display_line_length)
                    {
                        move_cursor_to(display_line_length);
                    }
                    break;
                case "home":
                    // set DDRAM address to first line 0
                    // check if 1 or two segments
                    if (set_segment_num == 1)
                    {
                        line = 1;
                    }
                    else if (set_segment_num == 2)
                    {
                        if (line < 3) { line = 1; }
                        if (line > 2) { line = 3; }
                     }
                    commit_instruction(cursorHomeTag);
                    break;
             }
        }

        /// <summary>
        /// Move Cursor to Specified Position
        /// </summary>
        /// <param name="position">Cursor position</param> 
        public void move_cursor_to(int position)
        {
            // don't go past DDRAM Display boundries
            // prevents line wrap 
            if (position > display_line_length) 
            {
                position = display_line_length;
            }
            else if (position < 0) 
            {
                position = 0;
            }


            // get current line number
            int lineNum = line;
            
            // determine initial instruction number
            // and add position to it
            if (lineNum == 1)
            {
                position += line1Offset ; // subtract DDRAM Address
            }
            else if (lineNum == 2)
            {
                position += line2Offset; // subtract DDRAM Address
            }
            else if (lineNum == 3)
            {
                position += line3Offset; // subtract DDRAM Address
            }
            else if (lineNum == 4)
            {
                position += line4Offset; // subtract DDRAM Address
            }

            // turn position into byte
            byte[] instNum = { (byte)position };

            // uses commit_instruction to avoid segment
            commit_instruction(instNum);
        } 
        
        #endregion

        #region Write text to LCD (line)

        /// <summary>
        /// Write Text to LCD<param name="text">Text</param>
        /// </summary> 
        public void write(string text)
        {
            // if cursor not set to zero, cut length
            bool tooFar = false;
            int curPos = get_cursor_position();
            int offSet;
            if (curPos > 0) { offSet = display_line_length - curPos; } else { offSet = display_line_length;  }

            // cut text down to line length
            // prevents getting lost in DDRAM
            if (offSet < text.Length) {
                tooFar = true; // text went too far
                text = text.Substring(0, offSet + 1);
            }

            // turn text into byteArray
            ASCIIEncoding encoding = new ASCIIEncoding();
            byte[] textToSend = encoding.GetBytes(text);

            // set CharTag to start text
            commit_write(textToSend);

            // prevents write function going too far
            if (tooFar) { move_cursor_to(display_line_length); }
        }

        /// <summary>
        /// Write Text to LCD at specified cursor position
        /// <param name="text">Text</param>
        /// <param name="line">LCD Line number</param>
        /// </summary>
        public void write(string text, int line)
        {
            // cut text down to line length
            // prevents getting lost in DDRAM
            if (display_line_length+1 <= text.Length) { text = text.Substring(0, display_line_length+1); }

            // set line to write to;
            this.line = line;

            // turn text into byteArray
            ASCIIEncoding encoding = new ASCIIEncoding();
            byte[] textToSend = encoding.GetBytes(text) ;

            // set CharTag to start text
            commit_write(textToSend);

            // prevents write function going too far
            if (get_cursor_position() > display_line_length) { move_cursor_to(display_line_length); }
        }
        
        /// <summary>
        /// For write functions sends data to microcontroller
        /// </summary>
        /// <param name="textByte">text to send</param>
        private void commit_write(byte[] textByte)
        {
            port.Write(charTag, 0, 1);
            Thread.Sleep(1);

            // interate thru text byteArray
            for (int i = 0; i < textByte.Length; i++)
            {
                port.Write(textByte, i, 1);
                Thread.Sleep(1);
            }

            // clear CharTag to stop text
            port.Write(charTag, 0, 1);
            Thread.Sleep(1);
        }
        
        #endregion

        #region Write User Defined Character To Display (line)

        /// <summary>
        /// Write User Defined Character to Display
        /// </summary>
        /// <param name="addr">Address of User Defined Character</param>
        /// <returns> True if Address of CGRAM 0-14 (hardware dependant) 15 is internally reserved</returns>
        public bool get_user_defined_character(int addr) 
        {
            // check if address is with in range
            // CGRAM address are 00000000 -> 00001110
            if (addr <= 14 && addr >= 0)
            { 
                int intNum = 0;

                intNum += addr;

                byte[] instNum = { (byte)intNum };

                commit_write(instNum);
                return true;
            }
            else
            {
                return false;
            }
        }
        
        #endregion

        #region Read Data (line)

        /// <summary>
        /// Read Character Rrom Current Cursor Position.
        /// </summary>
        /// <returns>Character</returns>
        public string read_character()
        {   
            // get current cursor position
            int curPos = get_cursor_position();
            int curLine = line;

            // create char array to get serial return value
            char[] retChar = new char[1];
            // send Read Instruction to microcontroller
            port.Write(readCharTag,0,1);
            Thread.Sleep(1);
            // get character out of buffer
            port.Read(retChar,0,1);

            // check to make sure read doesn't
            // push cursor past line max or min

            if (curPos == display_line_length && display_direction == "right")
            {
                line = curLine;
                move_cursor_to(0);
            }
            else if (curPos == 0 && display_direction == "left")
            {
                line = curLine;
                move_cursor_to(display_line_length);
            }

            // return string
            return retChar[0].ToString();
        }

        /// <summary>
        /// Get Cursor Position
        /// </summary>
        /// <returns>Cursor position</returns>
        public int get_cursor_position()
        {
            // create char array to get serial return value
            byte[] retByte = new byte[1];
            // send Read Data from AC
            port.Write(readAddressCounterTag, 0, 1);
            Thread.Sleep(1);
            // get address out of buffer
            port.Read(retByte, 0, 1);
            
            // convert to int
            int position = retByte[0];

            // if line 2 or 4 subtract 64 so output is 0-39
            //if (display_2_lines){
                if (line == 1)
                {
                    position -= line1ToZero;
                }
                else if (line == 2)
                {
                    position -= line2ToZero;
                }
                else if (line == 3)
                {
                    position -= line3ToZero;
                }
                else if (line == 4)
                {
                    position -= line4ToZero;
                }
           // }
            // return int
            return position;
        }
        
        #endregion

        #region Experimental Hardware Code
        /// <summary>
        /// Gets or sets backlight on or off
        /// </summary>
        /// <remarks>Is using PIC18F1230 to control backlight</remarks>
        public bool backlight_on
        { 
        get{ return backlight_on_h;}
        set
            {
                backlight_on_h = value;
                if (backlight_on)
                {
                    port.Write(backlightOnTag, 0, 1);
                    Thread.Sleep(1);
                }
                else
                {
                    port.Write(backlightOffTag, 0, 1);
                    Thread.Sleep(1);
               }
            
            }
        }
        #endregion

    } //end class
}// end namespace